﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  ApplicationEndpointProxy.cs
//    
// Abstract:
//
//  This class wraps all of the interaction that the Dispatcher needs
//  with an OCS application endpoint.
//  
//--

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Signaling;

namespace Microsoft.EnhancedReminders.Dispatcher
{
    /// <summary>
    /// This class wraps all of the interaction that the Dispatcher needs
    /// with an OCS application endpoint.
    /// </summary>
	public class ApplicationEndpointProxy : IOcsEndpointProxy
	{
        private ApplicationEndpoint _applicationEndpoint;
        private CollaborationPlatform _collabPlatform;

        #region IOcsEndpointProxy Members

        /// <summary>
        /// Event Handler for notifying listeners when the registration state of the
        /// SIP endpoint has changed.
        /// </summary>
        public event EventHandler<LocalEndpointStateChangedEventArgs> RegistrationStateChanged;

        /// <summary>
        /// Synchronously initialize the OcsEndpointProxy. This includes setting up the
        /// CollaborationPlatform and establishing the CollaborationEndpoint.
        /// </summary>
        public void Initialize()
        {
            string sipAddress = Helpers.GetSetting<string>("SipAddress", null);
            string sipServer = Helpers.GetSetting<string>("SipServer", null);
            string localHost = Helpers.GetSetting<string>("localhost", null);
            string applicationGruu = Helpers.GetSetting<string>("ApplicationGruu", null);
            int sipServerPort = Helpers.GetSetting<int>("SipServerPort", 5061);
            int applicationPort = Helpers.GetSetting<int>("ApplicationPort", 5068);

            if (sipAddress == null)
            {
                OperationalLogs.LogMissingSettingError("SipAddress");
                throw new ConfigurationErrorsException("Configuration does not specify a sip address");
            }

            if (sipServer == null)
            {
                OperationalLogs.LogMissingSettingError("SipServer");
                throw new ConfigurationErrorsException("Configuration does not specify a sip server");
            }

            if (localHost == null)
            {
                OperationalLogs.LogMissingSettingError("localhost");
                throw new ConfigurationErrorsException("Configuration does not specify the fully-qualified domain name of localhost");
            }

            if (applicationGruu == null)
            {
                OperationalLogs.LogMissingSettingError("ApplicationGruu");
                throw new ConfigurationErrorsException("Configuration does not specify an application gruu");
            }

            ApplicationEndpointSettings appSettings = new ApplicationEndpointSettings(sipAddress, sipServer, sipServerPort);
            appSettings.UseRegistration = true;
            ServerPlatformSettings platformSettings = 
                    new ServerPlatformSettings(Constants.EnhancedRemindersPlatformName, localHost, applicationPort, applicationGruu, ObtainCertificate());
            platformSettings.DefaultAudioVideoProviderEnabled = true;
            _collabPlatform = new CollaborationPlatform(platformSettings);
            _applicationEndpoint = new ApplicationEndpoint(_collabPlatform, appSettings);            
            _applicationEndpoint.StateChanged += ApplicationEndpointStateChanged;

            // Because this method should only be called on service startup when there is no work to be done,
            // we'll make our calls for establishing our endpoint synchronously.
            Logger.WriteTraceVerbose(Context.Global, "Initializing collaboration platform.");
            _collabPlatform.InstantMessagingSettings.SupportedFormats = InstantMessagingFormat.PlainText;
            _collabPlatform.EndStartup(_collabPlatform.BeginStartup(null, null));

            Logger.WriteTraceVerbose(Context.Global, "Initializing application endpoint.");
            _applicationEndpoint.EndEstablish(_applicationEndpoint.BeginEstablish(null, null));
        }

        /// <summary>
        /// Get the certificate required to register with OCS from the certificate store. The name of the cert should
        /// be the localhost name of the machine.
        /// </summary>
        public static X509Certificate2 ObtainCertificate()
        {
            List<X509Certificate2> list = new List<X509Certificate2>();

            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);

            foreach (X509Certificate2 cert in store.Certificates)
            {
                string name = cert.GetNameInfo(X509NameType.DnsName, false);

                if (string.Compare(Helpers.GetSetting<string>("localhost", null), name, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    list.Add(cert);
                }
            }

            if (list.Count == 0)
            {

                throw new ConfigurationErrorsException("Could not find certificate. Please make sure the certificate for registering with OCS was correctly installed.");
            }
            else
            {
                return list[0];
            }
        }

        /// <summary>
        /// Synchronously disconnects from OCS and shuts down the Collaboration Platform.
        /// </summary>
        public void Uninitialize()
        {
            Debug.Assert(_collabPlatform != null, "_collabPlatform != null");
            Debug.Assert(_applicationEndpoint != null, "_applicationEndpoint != null");

            if (_applicationEndpoint.State == LocalEndpointState.Established)
            {
                _applicationEndpoint.EndTerminate(_applicationEndpoint.BeginTerminate(null, null));
            }

            _collabPlatform.EndShutdown(_collabPlatform.BeginShutdown(null, null));       
        }

        /// <summary>
        /// Creates a new Conversation for implementations of IMessageSender to use
        /// </summary>
        public Conversation CreateConversation(string targetAddress)
        {
            Debug.Assert(_applicationEndpoint != null, "_applicationEndpoint != null");

            Conversation conversation = new Conversation(_applicationEndpoint);
            conversation.ApplicationContext = targetAddress;

            return conversation;
        }

        /// <summary>
        /// Obtains an appropriate subclass of IPresenceProvider for doing presence
        /// lookups.
        /// </summary>
        /// <returns>An instance of IPresenceProvider.</returns>
        public IPresenceProvider CreatePresenceProvider()
        {
            Debug.Assert(_applicationEndpoint != null, "_applicationEndpoint != null");
            
            return new UcmaPresenceProvider(_applicationEndpoint);
        }

        #endregion

        /// <summary>
        /// Notifies interested parties when the application endpoint state changes.
        /// </summary>
        /// <remarks>
        /// For ApplicationEndpointProxy this simply bubbles the state changed
        /// event so that the Dispatcher service can temporarily stop listening to MSMQ.
        /// Unlike ClientEndpoints, ApplicationEndpoints handle reconnecting to OCS on
        /// their own.
        /// </remarks>
        private void ApplicationEndpointStateChanged(object sender, LocalEndpointStateChangedEventArgs e)
        {            
            EventHandler<LocalEndpointStateChangedEventArgs> callback = RegistrationStateChanged;

            if (callback != null)
            {
                callback(this, e);
            }
        }
    }
}
